package io.github.lhdxhl.wechatpay.v3;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.github.lhdxhl.wechatpay.enums.WxAccountType;
import io.github.lhdxhl.wechatpay.enums.WxBillType;
import io.github.lhdxhl.wechatpay.enums.WxPayV3Type;
import io.github.lhdxhl.wechatpay.enums.WxServerType;
import io.github.lhdxhl.wechatpay.exception.PayException;
import io.github.lhdxhl.wechatpay.param.WxFundFlowBillParam;
import io.github.lhdxhl.wechatpay.param.WxTradeBillParam;
import io.github.lhdxhl.wechatpay.property.WxPayProperties;
import io.github.lhdxhl.wechatpay.result.WxDownloadBillResult;
import org.apache.http.Header;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.springframework.core.io.Resource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.net.URI;
import java.util.Objects;
import java.util.Optional;

/**
 * @author lsk
 * @version 2.0.0
 * @ClassName AbstractV3Api.java
 * @Description v3 api 接口基类
 * @createTime 2022年09月22日 14:53:00
 */
public abstract class AbstractV3Api {

    /**
     * The Mapper.
     */
    private final ObjectMapper mapper;

    /**
     * The Wechat pay  Client
     */
    private final WxPayClient  client;

    /**
     * 构造函数
     * @param client
     */
    public AbstractV3Api(WxPayClient client){
        this.mapper = new ObjectMapper();
        applyObjectMapper(mapper);
        this.client = client;
    }

    /**
     * get mapper
     * @return
     */
    public ObjectMapper getMapper() {
        return mapper;
    }

    /**
     * get client
     * @return
     */
    public WxPayClient client() {
        return client;
    }

    /**
     * get config
     * @return
     */
    public WxPayProperties getConfig() {
        return this.client.getConfig();
    }

    /**
     * @title applyObjectMapper
     * @description 数据格式化 mapper
     * @author lsk
     * @param: mapper
     * @updateTime 2022/10/5 17:01
     */
    private void applyObjectMapper(ObjectMapper mapper) {
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                // empty string error
                .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .registerModule(new JavaTimeModule());
    }

    /**
     * @title Post
     * @description post 请求
     * @author lsk
     * @param: uri
     * @param: params
     * @updateTime 2022/10/5 17:00
     * @return: org.apache.http.client.methods.HttpUriRequest
     */
    protected HttpUriRequest Post(URI uri, Object params) {
        HttpPost httpPost = new HttpPost(uri);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");
        try {
            httpPost.setEntity(new StringEntity(this.mapper.writeValueAsString(params), "UTF-8"));
            return httpPost;
        } catch (JsonProcessingException e) {
            throw new PayException("wechat app pay  failed : " + e);
        }
    }

    /**
     * @title Post
     * @description post 请求
     * @author lsk
     * @param: uri
     * @param: params
     * @param: headers
     * @updateTime 2022/10/5 17:00
     * @return: org.apache.http.client.methods.HttpUriRequest
     */
    protected HttpUriRequest Post(URI uri, Object params, Header[] headers) {
        HttpPost httpPost = new HttpPost(uri);
        httpPost.setHeaders(headers);
        try {
            httpPost.setEntity(new StringEntity(this.mapper.writeValueAsString(params), "UTF-8"));
            return httpPost;
        } catch (JsonProcessingException e) {
            throw new PayException("wechat app pay  failed : " + e);
        }
    }


    /**
     * @title Get
     * @description get 请求
     * @author lsk
     * @param: uri
     * @param: headers
     * @updateTime 2022/10/5 16:57
     * @return: org.apache.http.client.methods.HttpUriRequest
     */
    protected HttpUriRequest Get(URI uri, Header[] headers) {
        HttpGet httpGet = new HttpGet(uri);
        httpGet.setHeaders(headers);
        return httpGet;
    }


    /**
     * @title Get
     * @description get 请求
     * @author lsk
     * @param: uri
     * @updateTime 2022/10/5 16:58
     * @return: org.apache.http.client.methods.HttpUriRequest
     */
    protected HttpUriRequest Get(URI uri) {
        HttpGet httpGet = new HttpGet(uri);
        httpGet.addHeader("Accept", "application/json");
        return httpGet;
    }


    /**
     * @title billResDownload
     * @description 下载当前资源返回数据流
     * @author lsk
     * @param: linkUrl
     * @updateTime 2022/10/5 16:45
     * @return: io.github.lhdxhl.wechatpay.v3.WxResponseEntity<org.springframework.core.io.Resource>
     * @throws IOException
     */
    protected WxResponseEntity<Resource> billResDownload(String linkUrl) throws IOException {
        return this.client().withType(WxPayV3Type.FILE_DOWNLOAD, linkUrl)
                .function((type, downloadUrl) -> {
                    URI uri = UriComponentsBuilder.fromHttpUrl(downloadUrl)
                            .build()
                            .toUri();
                    return Get(uri);
                })
                .resource();
    }


    /**
     * @title billCsvDownload
     * @description 下载默认CSV，转字符串，注意去掉"`"
     * @author lsk
     * @param: linkUrl
     * @updateTime 2022/10/5 16:49
     * @return: java.lang.String
     * @throws
     */
    protected String billCsvDownload(String linkUrl) throws IOException {
        return this.client().withType(WxPayV3Type.FILE_DOWNLOAD, linkUrl)
                .function((type, downloadUrl) -> {
                    URI uri = UriComponentsBuilder.fromHttpUrl(downloadUrl)
                            .build()
                            .toUri();
                    return Get(uri);
                })
                .download();
    }

    /**
     * @title tradeBill
     * @description 获取账单请求的下载地址
     * @author lsk
     * @param: tradeBillParams
     * @updateTime 2022/10/5 17:13
     * @return: io.github.lhdxhl.wechatpay.v3.WxResponseEntity<io.github.lhdxhl.wechatpay.result.WxTradeBillResult>
     * @throws IOException
     */
    public WxResponseEntity<WxDownloadBillResult> tradeBill(WxTradeBillParam tradeBillParam) throws IOException {

        WxResponseEntity<WxDownloadBillResult> wxResponseEntity = new WxResponseEntity<>();

        this.client().withType(WxPayV3Type.TRADE_BILL, tradeBillParam)
                .function((wxPayV3Type, params) -> {
                    MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
                    queryParams.add("bill_date", tradeBillParam.getBillDate());
                    WxBillType tradeBillType = Optional.ofNullable(params.getBillType())
                            .orElse(WxBillType.ALL);
                    queryParams.add("bill_type", tradeBillType.name());
                    if (Objects.nonNull(tradeBillParam.getTarType())) {
                        queryParams.add("tar_type", tradeBillParam.getTarType());
                    }
                    URI uri = UriComponentsBuilder.fromHttpUrl(wxPayV3Type.uri(WxServerType.CHINA))
                            .queryParams(queryParams)
                            .build().toUri();
                    return Get(uri);
                })
                .consumer(responseEntity->{
                    ObjectNode body = (ObjectNode) responseEntity.getBody();
                    if (Objects.isNull(body)) {
                        throw new PayException("response body cannot be resolved");
                    }
                    WxDownloadBillResult wxTradeBillResult = this.getMapper().convertValue(body, WxDownloadBillResult.class);
                    if(Objects.isNull(wxTradeBillResult)){
                        throw new PayException("response body parse wxTradeBillResult is wrong");
                    }
                    wxResponseEntity.setHttpStatus(responseEntity.getHttpStatus());
                    wxResponseEntity.setBody(wxTradeBillResult);
                })
                .request();
        return wxResponseEntity;
    }

    /**
     * @title fundFlowBill
     * @description 申请资金账单API
     * @author lsk
     * @param: tradeBillParams
     * @updateTime 2022/10/5 17:13
     * @return: io.github.lhdxhl.wechatpay.v3.WxResponseEntity<io.github.lhdxhl.wechatpay.result.WxTradeBillResult>
     * @throws IOException
     */
    public WxResponseEntity<WxDownloadBillResult> fundFlowBill(WxFundFlowBillParam flowBillParam) throws IOException {

        WxResponseEntity<WxDownloadBillResult> wxResponseEntity = new WxResponseEntity<>();

        this.client().withType(WxPayV3Type.FUND_FLOW_BILL, flowBillParam)
                .function((wxPayV3Type, params) -> {
                    MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
                    queryParams.add("bill_date", params.getBillDate());
                    WxAccountType accountType = Optional.ofNullable(params.getAccountType())
                            .orElse(WxAccountType.BASIC);
                    queryParams.add("account_type", params.getAccountType().name());
                    if (Objects.nonNull(params.getTarType())) {
                        queryParams.add("tar_type", params.getTarType());
                    }
                    URI uri = UriComponentsBuilder.fromHttpUrl(wxPayV3Type.uri(WxServerType.CHINA))
                            .queryParams(queryParams)
                            .build().toUri();
                    return Get(uri);
                })
                .consumer(responseEntity->{
                    ObjectNode body = (ObjectNode) responseEntity.getBody();
                    if (Objects.isNull(body)) {
                        throw new PayException("response body cannot be resolved");
                    }
                    WxDownloadBillResult wxDownloadBillResult = this.getMapper().convertValue(body, WxDownloadBillResult.class);
                    if(Objects.isNull(wxDownloadBillResult)){
                        throw new PayException("response body parse wxDownloadBillResult is wrong");
                    }
                    wxResponseEntity.setHttpStatus(responseEntity.getHttpStatus());
                    wxResponseEntity.setBody(wxDownloadBillResult);
                })
                .request();
        return wxResponseEntity;
    }




}
